/*
===========================================================================

Wolfenstein: Enemy Territory GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Wolfenstein: Enemy Territory GPL Source Code (Wolf ET Source Code).  

Wolf ET Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Wolf ET Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Wolf ET Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

//===========================================================================
//
// Name:				l_mem.c
// Function:
// Programmer:		Mr Elusive (MrElusive@demigod.demon.nl)
// Last update:	1999-06-02
// Tab Size:		3
//===========================================================================

#include "qbsp.h"
#include "l_log.h"

int allocedmemory;

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void PrintMemorySize( unsigned long size ) {
	unsigned long number1, number2, number3;
	number1 = size >> 20;
	number2 = ( size & 0xFFFFF ) >> 10;
	number3 = ( size & 0x3FF );
	if ( number1 ) {
		Log_Print( "%ld MB", number1 );
	}
	if ( number1 && number2 ) {
		Log_Print( " and " );
	}
	if ( number2 ) {
		Log_Print( "%ld KB", number2 );
	}
	if ( number2 && number3 ) {
		Log_Print( " and " );
	}
	if ( number3 ) {
		Log_Print( "%ld bytes", number3 );
	}
} //end of the function PrintFileSize

#ifndef MEMDEBUG
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int MemorySize( void *ptr ) {
#if defined( WIN32 ) || defined( _WIN32 )
	#ifdef __WATCOMC__
	//Intel 32 bits memory addressing, 16 bytes aligned
	return ( _msize( ptr ) + 15 ) >> 4 << 4;
	#else
	return _msize( ptr );
	#endif
#else
	return 0;
#endif
} //end of the function MemorySize
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void *GetClearedMemory( int size ) {
	void *ptr;

	ptr = (void *) malloc( size );
	if ( !ptr ) {
		Error( "out of memory" );
	}
	memset( ptr, 0, size );
	allocedmemory += MemorySize( ptr );
	return ptr;
} //end of the function GetClearedMemory
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void *GetMemory( unsigned long size ) {
	void *ptr;
	ptr = malloc( size );
	if ( !ptr ) {
		Error( "out of memory" );
	}
	allocedmemory += MemorySize( ptr );
	return ptr;
} //end of the function GetMemory
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int fmemsize;
void FreeMemory( void *ptr ) {
	// RF, modified this for better memory trash testing
	fmemsize = MemorySize( ptr );
	allocedmemory -= fmemsize;

	// RF, somehow this crashes windows if size is less than or equal 8
	if ( fmemsize <= 8 ) {
		return;
	}

	// RF, set this memory to something that will cause problems if accessed again
	memset( ptr, 0xAA, fmemsize );

	free( ptr );
} //end of the function FreeMemory
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int TotalAllocatedMemory( void ) {
	return allocedmemory;
} //end of the function TotalAllocatedMemory

#else

#define MEM_ID      0x12345678l

int totalmemorysize;
int numblocks;

typedef struct memoryblock_s
{
	unsigned long int id;
	void *ptr;
	int size;
#ifdef MEMDEBUG
	char *label;
	char *file;
	int line;
#endif //MEMDEBUG
	struct memoryblock_s *prev, *next;
} memoryblock_t;

memoryblock_t *memory;

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void LinkMemoryBlock( memoryblock_t *block ) {
	block->prev = NULL;
	block->next = memory;
	if ( memory ) {
		memory->prev = block;
	}
	memory = block;
} //end of the function LinkMemoryBlock
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void UnlinkMemoryBlock( memoryblock_t *block ) {
	if ( block->prev ) {
		block->prev->next = block->next;
	} else { memory = block->next;}
	if ( block->next ) {
		block->next->prev = block->prev;
	}
} //end of the function UnlinkMemoryBlock
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
#ifdef MEMDEBUG
void *GetMemoryDebug( unsigned long size, char *label, char *file, int line )
#else
void *GetMemory( unsigned long size )
#endif //MEMDEBUG
{
	void *ptr;
	memoryblock_t *block;

	ptr = new char[size + sizeof( memoryblock_t )];
	block = (memoryblock_t *) ptr;
	block->id = MEM_ID;
	block->ptr = (char *) ptr + sizeof( memoryblock_t );
	block->size = size + sizeof( memoryblock_t );
#ifdef MEMDEBUG
	block->label = label;
	block->file = file;
	block->line = line;
#endif //MEMDEBUG
	LinkMemoryBlock( block );
	totalmemorysize += block->size;
	numblocks++;
	return block->ptr;
} //end of the function GetMemoryDebug
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
#ifdef MEMDEBUG
void *GetClearedMemoryDebug( unsigned long size, char *label, char *file, int line )
#else
void *GetClearedMemory( unsigned long size )
#endif //MEMDEBUG
{
	void *ptr;
#ifdef MEMDEBUG
	ptr = GetMemoryDebug( size, label, file, line );
#else
	ptr = GetMemory( size );
#endif //MEMDEBUG
	memset( ptr, 0, size );
	return ptr;
} //end of the function GetClearedMemoryLabelled
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void *GetClearedHunkMemory( unsigned long size ) {
	return GetClearedMemory( size );
} //end of the function GetClearedHunkMemory
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void *GetHunkMemory( unsigned long size ) {
	return GetMemory( size );
} //end of the function GetHunkMemory
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
memoryblock_t *BlockFromPointer( void *ptr, char *str ) {
	memoryblock_t *block;

	if ( !ptr ) {
#ifdef MEMDEBUG
		//char *crash = (char *) NULL;
		//crash[0] = 1;
		Error( "%s: NULL pointer\n", str );
#endif MEMDEBUG
		return NULL;
	} //end if
	block = ( memoryblock_t * )( (char *) ptr - sizeof( memoryblock_t ) );
	if ( block->id != MEM_ID ) {
		Error( "%s: invalid memory block\n", str );
	} //end if
	if ( block->ptr != ptr ) {

		Error( "%s: memory block pointer invalid\n", str );
	} //end if
	return block;
} //end of the function BlockFromPointer
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void FreeMemory( void *ptr ) {
	memoryblock_t *block;

	block = BlockFromPointer( ptr, "FreeMemory" );
	if ( !block ) {
		return;
	}
	UnlinkMemoryBlock( block );
	totalmemorysize -= block->size;
	numblocks--;
	//
	delete[] block;
} //end of the function FreeMemory
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int MemoryByteSize( void *ptr ) {
	memoryblock_t *block;

	block = BlockFromPointer( ptr, "MemoryByteSize" );
	if ( !block ) {
		return 0;
	}
	return block->size;
} //end of the function MemoryByteSize
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int MemorySize( void *ptr ) {
	return MemoryByteSize( ptr );
} //end of the function MemorySize
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void PrintUsedMemorySize( void ) {
	printf( "total botlib memory: %d KB\n", totalmemorysize >> 10 );
	printf( "total memory blocks: %d\n", numblocks );
} //end of the function PrintUsedMemorySize
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void PrintMemoryLabels( void ) {
	memoryblock_t *block;
	int i;

	PrintUsedMemorySize();
	i = 0;
	for ( block = memory; block; block = block->next )
	{
#ifdef MEMDEBUG
		Log_Write( "%6d, %p, %8d: %24s line %6d: %s", i, block->ptr, block->size, block->file, block->line, block->label );
#endif //MEMDEBUG
		i++;
	} //end for
} //end of the function PrintMemoryLabels
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void DumpMemory( void ) {
	memoryblock_t *block;

	for ( block = memory; block; block = memory )
	{
		FreeMemory( block->ptr );
	} //end for
	totalmemorysize = 0;
} //end of the function DumpMemory
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int TotalAllocatedMemory( void ) {
	return totalmemorysize;
} //end of the function TotalAllocatedMemory
#endif

//===========================================================================
// Q3 Hunk and Z_ memory management
//===========================================================================

typedef struct memhunk_s
{
	void *ptr;
	struct memhunk_s *next;
} memhunk_t;

memhunk_t *memhunk_high;
memhunk_t *memhunk_low;
int memhunk_high_size = 16 * 1024 * 1024;
int memhunk_low_size = 0;

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Hunk_ClearHigh( void ) {
	memhunk_t *h, *nexth;

	for ( h = memhunk_high; h; h = nexth )
	{
		nexth = h->next;
		FreeMemory( h );
	} //end for
	memhunk_high = NULL;
	memhunk_high_size = 16 * 1024 * 1024;
} //end of the function Hunk_ClearHigh
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void *Hunk_Alloc( int size ) {
	memhunk_t *h;

	if ( !size ) {
		return (void *) memhunk_high_size;
	}
	//
	h = GetClearedMemory( size + sizeof( memhunk_t ) );
	h->ptr = (char *) h + sizeof( memhunk_t );
	h->next = memhunk_high;
	memhunk_high = h;
	memhunk_high_size -= size;
	return h->ptr;
} //end of the function Hunk_Alloc
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void *Z_Malloc( int size ) {
	return GetClearedMemory( size );
} //end of the function Z_Malloc
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Z_Free( void *ptr ) {
	FreeMemory( ptr );
} //end of the function Z_Free
